﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Timers;

using HAVNet.Core;
using HAVNet.Core.IO;
using HAVNet.Core.Time;

using TeleVoyeur;
using LitJson;
using HAVNet.Core.Lambda;


namespace GPSServer
{
    class Program
    {
        static Dictionary<string, Ovni> _ovnis      = new Dictionary<string, Ovni>(); // key / ovni
        static object                   _lockOvnis  = new object();
        static System.Timers.Timer      _timer      = new System.Timers.Timer(4000);

        static void Main (string [] args)
        {
            _timer.Elapsed += new ElapsedEventHandler(timerTick);
            _timer.Enabled = true;
            _timer.Start();

            for (; ; )
            {
                Process          process = new Process ();
                ProcessStartInfo psi;

                if (args.Length == 2)
                {
                    psi = new ProcessStartInfo(
                        args[0],
                        args[1]
                    );

                    TSConsole.mostrarln("iniciado por parametros", ConsoleColor.Red);
                }
                else
                {
                    psi = new ProcessStartInfo(
                        @".\node.exe",
                        @""".\tcpserver.js"" 9000 1000"
                    );

                    TSConsole.mostrarln("iniciado por defecto", ConsoleColor.Blue);
                }

                psi.RedirectStandardOutput  = true;
                psi.UseShellExecute         = false;
                process.StartInfo           = psi;
                process.Start               ();

                using (StreamReader sr = process.StandardOutput)
                {
                    string linea = "";

                    while (null != (linea = sr.ReadLine()))
                    {
                        if (linea.StartsWith("conec") || linea.StartsWith("close"))
                        {
                            TSConsole.mostrarln (linea, ConsoleColor.Green);
                        }
                        else if (linea.StartsWith("data"))
                        {
                            TSConsole.mostrarln (linea, ConsoleColor.DarkCyan);
                        }
                        else if (linea.StartsWith("alive"))
                        {
                            TSConsole.mostrar   (linea, ConsoleColor.DarkGray);
                            TSConsole.mostrarln (string.Format(" [{0:dd/MM/yyyy HH:mm:ss}]", System.DateTime.Now), ConsoleColor.DarkYellow);
                        }
                        else
                        {
                            TSConsole.mostrarln (linea, ConsoleColor.DarkGray);
                        }

                        // proceso linea data...
                        if (linea.StartsWith("data"))
                        {
                            procesarData(linea);
                        }
                    }

                    sr.Close();
                }

                Errors.ignore   (() => { process.Kill(); });
                Thread.Sleep    (1000);
            }
        }

        static void timerTick (object sender, ElapsedEventArgs e)
        {
            _timer.Enabled          = false;
            GrupoOvnis grupoOvnis   = null;                     // <<----- dejar asi

            lock (_lockOvnis)
                if (_ovnis.Count > 0)
                    grupoOvnis = new GrupoOvnis(_ovnis);        // <<----- dejar asi

            if (grupoOvnis != null)                             // <<----- dejar asi
                meterOvnisEnPaginaWeb (
                    grupoOvnis,
                    () => { _timer.Enabled = true; }
                );
            else
                _timer.Enabled = true;                          // <<----- dejar asi
        }

        private static void meterOvnisEnPaginaWeb (GrupoOvnis grupoOvnis, VFunc fin)
        {
            string json = "";

            // trato de serializar los ovnis
            try
            {
                json = JsonMapper.ToJson (grupoOvnis);
                TSConsole.mostrarln (json);
            }
            catch (Exception exx)
            {
                TSConsole.mostrarln ("Error serializando!", ConsoleColor.Red);
                TSConsole.mostrarln (exx.Message, ConsoleColor.Red);
                // llamo fin
                fin ();
                return;
            }

            ThreadPool.QueueUserWorkItem (new WaitCallback((o) =>
            {
                // trato de usar el updater de la pagina web...
                string respuestaWeb = WebUpdater.jsonRequestPost (json);

                if (string.IsNullOrEmpty(respuestaWeb))
                {
                    // llamo fin
                    TSConsole.mostrarln ("Respuesta web vacia", ConsoleColor.Red);
                    fin ();
                    return;
                }

                // muestro la respuesta
                TSConsole.mostrarln (respuestaWeb, ConsoleColor.Magenta);

                // trato de de-serializar
                Dictionary<string, Ovni> upsertados;
                try
                {
                    upsertados = JsonMapper.ToObject<Dictionary<string, Ovni>>(respuestaWeb);
                }
                catch (Exception exx)
                {
                    TSConsole.mostrarln ("JSON deforme en respuesta web -> " + respuestaWeb, ConsoleColor.Red);
                    TSConsole.mostrarln (exx.Message, ConsoleColor.Red);
                    fin ();
                    return;
                }

                // borro los ovnis que acabo de inserté bien segun la respuesta
                Errors.ignore(() => {
                    if (upsertados != null)
                        lock (_ovnis)
                            foreach (KeyValuePair<string, Ovni> kvp in upsertados)
                                if (_ovnis.ContainsKey(kvp.Key) && _ovnis[kvp.Key].Equals(kvp.Value))
                                    _ovnis.Remove(kvp.Key);
                });

                // llamo fin
                fin ();
            }));
        }

        static void procesarData (string linea)
        {
            //  ejemplo 
            //  imei:359587010124900,tracker,0809231929,13554900601,F,112909.397,A,2234.4669,N,11354.3287,E,0.11,;
            if (linea.Contains("tracker"))
            {
                procesarLineaTracker(linea);
            }
        }

        static void procesarLineaTracker (string linea)
        {
            const int LAT_DEG_LENGTH = 2;
            const int LON_DEG_LENGTH = 3;

            // sacar imei
            string  imei    = Regex.Match(linea, "[0-9]{15,}").Value;
            if (imei == string.Empty) {
                TSConsole.mostrarln("fallo imei en procesarLineaTracker", ConsoleColor.Red);
                return;
            }

            // sacar latitud
            string  slat    = Regex.Match(linea, "[0-9]{4}.[0-9]+,[N|S]").Value;
            if (slat == string.Empty) {
                TSConsole.mostrarln("fallo slat en procesarLineaTracker", ConsoleColor.Red);
                return;
            }
            int     signoLat= (slat.Contains("N") ? 1 : -1);
            string  snlat   = slat.Substring(0, slat.Length - 2); // saco ",X"
            double  lat     = (
                double.Parse(snlat.Substring(0, LAT_DEG_LENGTH)) +
                double.Parse(snlat.Substring(LAT_DEG_LENGTH, snlat.Length - LAT_DEG_LENGTH), CultureInfo.InvariantCulture.NumberFormat) / 60.0
                ) * signoLat;

            // sacar longitud
            string  slon    = Regex.Match(linea, "[0-9]{5}.[0-9]+,[W|E]").Value;
            if (slon == string.Empty) {
                TSConsole.mostrarln("fallo slon en procesarLineaTracker", ConsoleColor.Red);
                return;
            }
            int     signoLon= (slon.Contains("E") ? 1 : -1);
            string  snlon   = slon.Substring(0, slon.Length - 2); // saco ",X"
            double  lon     = (
                double.Parse(snlon.Substring(0, LON_DEG_LENGTH)) +
                double.Parse(snlon.Substring(LON_DEG_LENGTH, snlat.Length - LON_DEG_LENGTH), CultureInfo.InvariantCulture.NumberFormat) / 60.0
                ) * signoLon;

            // sacar velocidad e inclinación
            string [] partes = linea.Split(',');
            double vel = 0.0;
            double bea = 0.0;

            if (partes.Length == 14)
            {
                // velocidad
                if (! string.IsNullOrEmpty(partes[11]))
                {
                    vel = double.Parse(partes[11], CultureInfo.InvariantCulture.NumberFormat);
                    vel *= 1.609; // viene en millas
                }

                // inclinación
                if (! string.IsNullOrEmpty(partes[12]))
                {
                    bea = double.Parse(partes[12], CultureInfo.InvariantCulture.NumberFormat);
                }
            }

            try
            {
                ServerFiles.escribirArchivo (imei, lat, lon, vel, bea);
            }
            catch
            {
                TSConsole.mostrarln ("fallo archivo!", ConsoleColor.Red);
            }
            
            agregarOvni (
                imei,
                new Ovni (
                    UnixTime.dateTime2FechaUNIX(DateTime.Now).ToString(), // el tiempo en unix
                    imei, // key
                    lat.ToString("##0.00000", CultureInfo.InvariantCulture.NumberFormat),
                    lon.ToString("##0.00000", CultureInfo.InvariantCulture.NumberFormat),
                    vel.ToString("##0.00"   , CultureInfo.InvariantCulture.NumberFormat),
                    ((int)bea).ToString()
                )
            );
        }

        private static void agregarOvni (string imei, Ovni ovni)
        {
            lock (_lockOvnis)
                if (_ovnis.ContainsKey(imei))
                    _ovnis[imei] = ovni;
                else
                    _ovnis.Add(imei, ovni);
        }
    }
}
